如何使用带有代码优先迁移的 Fluent API 设置外键名称?

How can I set the foreign key name using the Fluent API with Code First Migrations?

我正在尝试使用 EF6.4 上的代码优先迁移设置外键名称(不是外键列)。​​

我知道可以通过更新生成的迁移代码来设置,像这样:

.ForeignKey("Documents", Function(t) t.DocumentId, cascadeDelete:=True, name:="FK_Sections_Documents")

...但我想在添加迁移之前使用 Fluent API.

我似乎记得一些关于 HasForeignKey() 调用接受 Func 的事情,它在其主体中包含对匿名类型的调用,例如我们发现的 here。但是,如果我能找到任何讨论该类型的一般结构应该是什么的东西,我会很高兴的。

官方文档没有讨论:

这些类似的问答也没有完全解决问题:

几个月前有人问过同样的问题here,但到目前为止还没有收到答案。

我正在使用 EntityTypeConfiguration(Of T)。这是我的代码:

Namespace Configuration
  Friend Class SectionConfig
    Inherits EntityTypeConfiguration(Of Db.Section)

    Public Sub New()
      Me.HasRequired(Function(Section) Section.Document).WithMany.HasForeignKey(Function(Section) Section.DocumentId)

      Me.Property(Function(Section) Section.DocumentId).IsRequired()
      Me.Property(Function(Section) Section.SectionId).IsRequired()
      Me.Property(Function(Section) Section.IsSent).IsRequired()
      Me.Property(Function(Section) Section.Markup).IsRequired.IsMaxLength()
      Me.Property(Function(Section) Section.Title).IsRequired.HasMaxLength(60)

      Me.HasIndex(Function(Section) Section.DocumentId).HasName("IX_Sections_DocumentId")
      Me.HasIndex(Function(Section) Section.SectionId).HasName("IX_Sections_SectionId")
      Me.HasIndex(Function(Section) Section.Title).HasName("IX_Sections_Title")

      Me.Ignore(Function(Section) Section.Subject)
    End Sub
  End Class
End Namespace

如何设置外键名称,或者——甚至更具体,假设我没记错的话——匿名类型的一般结构应该是什么?

--更新--

我试过这个:

Me.HasRequired(Function(Section) Section.Document).WithMany.HasForeignKey(Function(Section) New With {.DependentKeyExpression = Section.DocumentId, .Name = "FK_Sections_Documents"})

...但是创建迁移的尝试是这样回答的:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: The properties expression 'Section => new VB$AnonymousType_0`2(DependentKeyExpression = Section.DocumentId, Name = "FK_Sections_Documents")' is not valid. The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple properties use an anonymous type: C#: 't => new { t.MyProperty1, t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1, t.MyProperty2 }'.

因此,鉴于匿名类型构造用于指定键列,这不是指定外键名称的方法。

问题依旧:如何使用EF6.4中的FluentAPI指定外键名?

使用 Fluent Mappings,ForeignKey 属性期望您的 class 中的 属性 名称作为参数。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<WidgetEntity>()
   .HasRequired(w => w.Sequence)
   .WithMany()
   .Map(m => m.MapKey("FK_Sections_Documents"));
}

查找数据配置:

HasRequired(p => p.LookupType).WithMany(p=>p.LookupData).HasForeignKey(p=>p.LookupTypeId).WillCascadeOnDelete(false);

名称配置:

HasOptional(p => p.Gender).WithMany(p=>p.Name).HasForeignKey(p=>p.GenderLookupId).WillCascadeOnDelete(false);

如果您不想使用流畅的语法,您也可以使用 ,因为 Inverse 属性 更易于阅读并且写在 属性 之上他们正在影响。

更新:

Use ForeignKey with an associated property

示例 1:

[Table("ENTITIES")]
public class Entity {

    [Column("ENTITY_NO")]
    public int No { set; get; }

    [Column("SEQUENCE_NO")]
    public int SequenceNo { set; get; }

    [ForeignKey("SequenceNo")] //Has to be a property name, not table column name
    public Sequence Sequence { set; get; }

    // and other properties that map correctly
}

[Table("SEQUENCES")]
public class Sequence { 

    [Column("SEQUENCE_NO")]
    public int No { set; get; }

    [Column("NUMBER")]
    public int Number { set; get; }
}

示例 2:

[Table("ENTITIES")]
public class Entity {

    [Column("ENTITY_NO")]
    public int No { set; get; }

    [ForeignKey("Sequence")] //Has to be a property name, not table column name
    [Column("SEQUENCE_NO")]
    public int SequenceNo { set; get; }

    public Sequence Sequence { set; get; }

    // and other properties that map correctly
}

[Table("SEQUENCES")]
public class Sequence { 

    [Column("SEQUENCE_NO")]
    public int No { set; get; }

    [Column("NUMBER")]
    public int Number { set; get; }
}

遗憾的是,在 EF6 中没有执行此操作的内置方法。为了稍微澄清您的问题,您要问的是如何指定基于模型中指定的外键创建的基础数据库约束的名称。这将是迁移生成器的工作。尽管 EF6 迁移中存在传递约束名称的功能,但如果您浏览源代码,您会发现它从未实际使用过,并且所有方法都是私有和内部的。

这已在 EFCore 中重新说明,您现在可以在其中执行以下操作: Me.HasRequired(Function(Section) Section.Document).WithMany().HasForeignKey(Function(Section) Section.DocumentId).HasConstraintName("FK_Sections_Documents")但这对你没有帮助。

所以您需要编写一个自定义迁移生成器来执行您想要的操作。在这个 post: Change foreign key constraint naming convention 中回答了这个确切的问题(C# 中的代码,数据库是 SQL 服务器)